package com.beesphere.excel.writer.style;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;

import com.beesphere.excel.writer.impls.WorkbookContext;
import com.beesphere.excel.writer.style.resolvers.BasicResolver;
import com.beesphere.excel.writer.style.resolvers.BooleanResolver;
import com.beesphere.excel.writer.style.resolvers.ColorResolver;
import com.beesphere.excel.writer.style.resolvers.DataFormatResolver;
import com.beesphere.excel.writer.style.resolvers.FontResolver;
import com.beesphere.excel.writer.style.resolvers.ShortResolver;
import com.beesphere.excel.writer.style.resolvers.StaticFieldBasedStyleResolver;
import com.qlogic.commons.utils.json.JsonException;
import com.qlogic.commons.utils.json.JsonObject;

public class JsonCellStyleRegistry implements CellStyleRegistry {
	
	private static final long serialVersionUID = 3049246464200990255L;

	protected Map<String, JsonObject> styles;
	
	public JsonCellStyleRegistry () {
	}
	
	public JsonCellStyleRegistry (String styles) throws CellStyleSpecReaderException {
		install (styles);
	}
	
	public static final Map<String, StyleResolver> RESOLVERS = new HashMap<String, StyleResolver> ();
	static {
		try {
			//General
			RESOLVERS.put("rotation", new ShortResolver (CellStyle.class.getDeclaredMethod("setRotation", short.class)));
			RESOLVERS.put("indention", new ShortResolver (CellStyle.class.getDeclaredMethod("setIndention", short.class)));

			RESOLVERS.put("hidden", new BooleanResolver (CellStyle.class.getDeclaredMethod("setHidden", boolean.class)));
			RESOLVERS.put("locked", new BooleanResolver (CellStyle.class.getDeclaredMethod("setLocked", boolean.class)));
			RESOLVERS.put("alignment", new StaticFieldBasedStyleResolver (CellStyle.class.getDeclaredMethod("setAlignment", short.class), "ALIGN_"));
			RESOLVERS.put("wrapText", new BooleanResolver (CellStyle.class.getDeclaredMethod("setWrapText", boolean.class)));
			RESOLVERS.put("verticalAlignment", new StaticFieldBasedStyleResolver (CellStyle.class.getDeclaredMethod("setVerticalAlignment", short.class), "VERTICAL_"));
			RESOLVERS.put("border.left.type", new StaticFieldBasedStyleResolver (CellStyle.class.getDeclaredMethod("setBorderLeft", short.class), "BORDER_"));
			RESOLVERS.put("border.right.type", new StaticFieldBasedStyleResolver (CellStyle.class.getDeclaredMethod("setBorderRight", short.class), "BORDER_"));
			RESOLVERS.put("border.top.type", new StaticFieldBasedStyleResolver (CellStyle.class.getDeclaredMethod("setBorderTop", short.class), "BORDER_"));
			RESOLVERS.put("border.bottom.type", new StaticFieldBasedStyleResolver (CellStyle.class.getDeclaredMethod("setBorderBottom", short.class), "BORDER_"));
			RESOLVERS.put("border.left.color", new ColorResolver (CellStyle.class.getDeclaredMethod("setLeftBorderColor", short.class)));
			RESOLVERS.put("border.right.color", new ColorResolver (CellStyle.class.getDeclaredMethod("setRightBorderColor", short.class)));
			RESOLVERS.put("border.top.color", new ColorResolver (CellStyle.class.getDeclaredMethod("setTopBorderColor", short.class)));
			RESOLVERS.put("border.bottom.color", new ColorResolver(CellStyle.class.getDeclaredMethod("setBottomBorderColor", short.class)));
			RESOLVERS.put("fillBackgroundColor", new ColorResolver (CellStyle.class.getDeclaredMethod("setFillBackgroundColor", short.class)));
			RESOLVERS.put("fillForegroundColor", new ColorResolver (CellStyle.class.getDeclaredMethod("setFillForegroundColor", short.class)));
			
			RESOLVERS.put("fillPattern", new StaticFieldBasedStyleResolver (CellStyle.class.getDeclaredMethod("setFillPattern", short.class), null));
			
			RESOLVERS.put("font", new FontResolver (CellStyle.class.getDeclaredMethod("setFont", Font.class)));
			RESOLVERS.put("format", new DataFormatResolver (CellStyle.class.getDeclaredMethod("setDataFormat", short.class)));
			
			// FONTS
			RESOLVERS.put("font.name", new BasicResolver (Font.class.getDeclaredMethod("setFontName", String.class)));
			RESOLVERS.put("font.height", new ShortResolver (Font.class.getDeclaredMethod("setFontHeight", short.class)));
			RESOLVERS.put("font.heightInPoints", new ShortResolver (Font.class.getDeclaredMethod("setFontHeightInPoints", short.class)));
			RESOLVERS.put("font.italic", new BooleanResolver (Font.class.getDeclaredMethod("setItalic", boolean.class)));
			RESOLVERS.put("font.strikeout", new BooleanResolver (Font.class.getDeclaredMethod("setStrikeout", boolean.class)));
			RESOLVERS.put("font.color", new ColorResolver (Font.class.getDeclaredMethod("setColor", short.class)));
			RESOLVERS.put("font.typeOffset", new ShortResolver (Font.class.getDeclaredMethod("setTypeOffset", short.class)));
			RESOLVERS.put("font.underline", new StaticFieldBasedStyleResolver (Font.class.getDeclaredMethod("setUnderline", byte.class), "U_"));
			RESOLVERS.put("font.charSet", new StaticFieldBasedStyleResolver (Font.class.getDeclaredMethod("setCharSet", byte.class), null, "_CHARSET"));
			RESOLVERS.put("font.boldweight", new StaticFieldBasedStyleResolver (Font.class.getDeclaredMethod("setBoldweight", short.class), "BOLDWEIGHT_"));
		} catch (Throwable e) {
			e.printStackTrace();
		} 
	}

	@Override
	public void install (String spec) throws CellStyleSpecReaderException {
		if (spec == null || spec.trim ().isEmpty ()) {
			return;
		}
		if (styles == null) {
			styles = new HashMap<String, JsonObject> ();
		}
		try {
			JsonObject stylesSpec = new JsonObject (spec);
			Iterator<String> stylesNames = stylesSpec.keys ();
			String name;
			Object value;
			while (stylesNames.hasNext ()) {
				name = stylesNames.next ();
				value = stylesSpec.get (name);
				if (value != null) {
					if (value instanceof JsonObject) {
						styles.put (name, (JsonObject)value);
					}
				}
			}
		} catch (JsonException e) {
			throw new CellStyleSpecReaderException (e);
		}
	}
	
	@Override
	public void uninstall (String styleName) {
		if (styles == null) {
			return;
		}
		styles.remove (styleName);
	}

	public JsonObject get (String styleName) {
		if (styles == null) {
			return null;
		}
		return styles.get (styleName);
	}

	@Override
	public CellStyle create (String styleName, WorkbookContext workbookContext) {
		CellStyle cellStyle = workbookContext.getWorkbook ().createCellStyle ();
		build (cellStyle, get (styleName), workbookContext);
		return cellStyle;
	}

	@Override
	public void reset () {
		if (styles != null) {
			styles.clear();
		}
	}

	private void build (CellStyle cellStyle, JsonObject styleSpec, WorkbookContext workbookContext) {
		build (cellStyle, styleSpec, null, workbookContext);
	}
	
	private void build (Object applyTo, JsonObject styleSpec, String path, WorkbookContext workbookContext) {
		Iterator<String> it = styleSpec.keys ();
		while (it.hasNext()) {
			String name = it.next ();
			Object value = styleSpec.get (name);
			if (value == null) {
				continue;
			}
			String newPath;
			if (path == null) {
				newPath = name;
			} else {
				newPath = path + "." + name;
			}
			StyleResolver resolver = RESOLVERS.get (newPath);
			if (resolver == null) {
				continue;
			}
			if (value instanceof JsonObject) {
				Object bean = resolver.create (workbookContext);
				if (bean != null) {
					build (bean, (JsonObject) value, newPath, workbookContext);
					resolver.apply (workbookContext, applyTo, newPath, bean);
				} else {
					build (applyTo, (JsonObject) value, newPath, workbookContext);
				}
			} else {
				resolver.apply (workbookContext, applyTo, newPath, value);
			}
		}
	}

}
